package jfprincesstests.structures.negotiation;

import java.util.Arrays;
import java.util.List;

import jfprincesstests.fakes.FakeCmd;
import jfprincesstests.fakes.FakeCondition;
import jfprincesstests.fakes.FakeForAgetn;
import jfprincesstests.fakes.FakeGetIntService;
import jfprincesstests.fakes.IFakeGetIntService;
import jfuzzyprincepless.agents.BaseNegotiationAgent;
import jfuzzyprincepless.agents.INegotiatingAgent;
import jfuzzyprincepless.agents.RegisterServicesAgent;
import jfuzzyprincepless.zarchive.algorithm.structure.ChainCommand;
import jfuzzyprincepless.zarchive.algorithm.structure.ICommandConstruction;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.IForConstruction;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.NegotiationForAbsEach;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.INegotiationCondition;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.NegotiationAndCondition;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.NegotiationIfConstruction;
import jfuzzyprincepless.negotiation.impl.BaseNegotiation;
import jfuzzyprincepless.negotiation.DefaultNegotiation;
import jfuzzyprincepless.negotiation.DefaultNegotiationContext;
import jfuzzyprincepless.zarchive.negotiation.IAgentsExporter;
import jfuzzyprincepless.zarchive.negotiation.services.IExecuteForService;
import jfuzzyprincepless.zarchive.negotiation.services.IExecuteIfService;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.ExecuteIfNegotiation;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.DefaultNegotiation__Q;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.ExecuteForNegotiation;
import jfuzzyprincepless.negotiation.IExecutionContext;
import jfuzzyprincepless.negotiation.INegotiation;
import jfuzzyprincepless.negotiation.INegotiationContext;
import laboratoryQ.computation.BaseUtils;
import laboratoryQ.junit.ignore.patch.BaseTestCase;

public class TestNegotiation extends BaseTestCase {
	public void testDefaultNegotiation() {
		StringBuilder sb = new StringBuilder();
		
		INegotiatingAgent agent0 = new FakeCondition(0.9, true);
		INegotiatingAgent agent1 = new FakeCondition(0.1, true);
		List<ICommandConstruction> thenCmd =
			Arrays.<ICommandConstruction>asList(
				new FakeCmd(sb, "thenCmd")
			);
		List<ICommandConstruction> elseCmd =
			Arrays.<ICommandConstruction>asList(
				new FakeCmd(sb, "elseCmd")
			);
		RegisterServicesAgent rsAgent = new RegisterServicesAgent(
			new ExecuteIfNegotiation(
				new NegotiationIfConstruction(null, thenCmd, elseCmd)
			)
		);
		
		// ### ### ### ### ### ### ### ### ### ### ### ### ### ###
		
		INegotiation defaultNegotiation = new DefaultNegotiation();
		INegotiationContext context = new DefaultNegotiationContext(
			Arrays.<INegotiatingAgent>asList(
				agent0,
				agent1,
				rsAgent
			)
		);
		defaultNegotiation.negotiate(context);
		assertEquals("thenCmd;elseCmd;", sb.toString());
		
		sb.setLength(0);
		((FakeCondition)agent0).setTruthDegree(0.1);
		((FakeCondition)agent1).setTruthDegree(0.9);
		defaultNegotiation.negotiate(context);
		assertEquals("elseCmd;thenCmd;", sb.toString());
	}
	
	// TODO: review DefaultNegotiation__Q
	public void testDefaultNeg_Q_resultCommandsRun() {
		final StringBuilder sb = new StringBuilder();

		INegotiation negotiation = new DefaultNegotiation__Q(
			new ChainCommand(
				new FakeCmd(sb, "cmd0"),
				new FakeCmd(sb, "cmd1"),
				new FakeCmd(sb, "cmd2")
			),
			IAgentsExporter.EMPTY_EXPORTER
		);

		IExecutionContext context = negotiation.negotiate();
		negotiation.execute(context);
		assertEquals("cmd0;cmd1;cmd2;", sb.toString());
	}

	private static class BaseNegotiationImpl
		extends BaseNegotiation
		implements IExecuteForService<Integer>
	{
		private INegotiationContext context;

		@Override
		public void execute(IExecutionContext context) {
			throw new UnsupportedOperationException("not implemented");

		}
		@Override
		public void negotiate(INegotiationContext context) {
			context.registerService(this);
			assertIsInstance(context, DefaultNegotiationContext.class);
			this.context = context;
		}
//		@Override
//		public List<INegotiatingAgent> getAgents() {
//			throw new UnsupportedOperationException("not implemented");
//		}
		@Override
		public Integer getCurrentElem() {
			throw new UnsupportedOperationException("not implemented");
		}
		@Override
		public IForConstruction<Integer> getForConstruction() {
			throw new UnsupportedOperationException("not implemented");
		}
	}

	public void testBaseNegotiation() {
		BaseNegotiationImpl negotiation = new BaseNegotiationImpl();
		negotiation.negotiate();
		assertNotNull(negotiation.context);
		assertNotNull(negotiation.context.getService(IExecuteForService.class));
	}

	public void testContextstIsolation() {
		final StringBuilder sb = new StringBuilder();
		final INegotiationCondition globalIfCondition =
			new NegotiationAndCondition();

		NegotiationForAbsEach<Integer> forLoop = new NegotiationForAbsEach<Integer>(
			Arrays.asList(1, 2, 3),
			ICommandConstruction.EMPTY_ARRAY,
			Arrays.<INegotiatingAgent>asList(
				new FakeForAgetn<Integer>(sb, ";for-neg"),
				new BaseNegotiationAgent() {
					@Override
					public boolean negotiate(INegotiationContext context) {
						IExecuteIfService ifServ =
							context.getService(IExecuteIfService.class);
						if (ifServ != null) {
							sb.append(";for-ag--if-detected");
							assertSame(
								globalIfCondition,
								ifServ.getIfConstruction().getCondition()
							);
						}
						return super.negotiate(context);
					}
				}
			)
		);

		NegotiationIfConstruction ifConstr = new NegotiationIfConstruction(
			INegotiationCondition.EMPTY_CONDITION,
			ICommandConstruction.EMPTY_LIST,
			ICommandConstruction.EMPTY_LIST,
			Arrays.<INegotiatingAgent>asList(
				new BaseNegotiationAgent() {
					@SuppressWarnings("unchecked")
					@Override
					public boolean negotiate(INegotiationContext context) {
						IExecuteForService<Integer> forServ = context.getService(IExecuteForService.class);
						if (forServ != null) {
							sb.append("if-ag--elem=").append(forServ.getCurrentElem());
						} else {
							sb.append(";if-ag--no-for-found");
						}
						return super.negotiate(context);
					}
				}
			)
		);

		ChainCommand theProgram = new ChainCommand(
			forLoop,
			ifConstr,
			forLoop
		);

		INegotiationContext context = new DefaultNegotiationContext();
		context.registerService(new FakeGetIntService(234));

		theProgram.execute(context);
		assertEquals(
			234,
			context.getService(IFakeGetIntService.class).getInt()
		);
		assertEquals(
			";for-neg1;for-neg2;for-neg3;if-ag--no-for-found" +
			";for-neg1;for-neg2;for-neg3",
			sb.toString()
		);

		// global for-service
		sb.setLength(0);

		context = new DefaultNegotiationContext();
		ExecuteForNegotiation<Integer> fakeForNeg =
			new ExecuteForNegotiation<Integer>(
				new NegotiationForAbsEach<Integer>(
					Arrays.asList(1, 2, 3)
				)
			);
		Object forLoopExecutor = BaseUtils.safeGetFieldVal(fakeForNeg, "forLoopExecutor");
		BaseUtils.safeSetFieldVal(forLoopExecutor, true, "isRun");
		BaseUtils.safeSetFieldVal(forLoopExecutor, 987, "current");

		ExecuteIfNegotiation fakeIfNeg = new ExecuteIfNegotiation(
			new NegotiationIfConstruction(
				globalIfCondition,
				ICommandConstruction.EMPTY_LIST,
				ICommandConstruction.EMPTY_LIST
			)
		);

		context.registerService(fakeForNeg);
		context.registerService(fakeIfNeg);
		theProgram.execute(context);
		assertEquals(
			";for-neg1;for-ag--if-detected" +
			";for-neg2;for-ag--if-detected" +
			";for-neg3;for-ag--if-detected" +
			"if-ag--elem=987" +
			";for-neg1;for-ag--if-detected" +
			";for-neg2;for-ag--if-detected" +
			";for-neg3;for-ag--if-detected",
			sb.toString()
		);
	}
}
